﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Nvigorate.Data;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.Relational.UnitOfWork;
using Nvigorate.TestObjects;

namespace Nvigorate.Test.Relational.UnitOfWork
{
    [TestClass]
    public class UnitOfWorkBaseTests
    {
        private IRepositoryMapIndex _repositoryMapIndex;

        [TestInitialize]
        public void TestInit()
        {
            MapFake.PopulateMaps();            
        }

        #region CreatePersistCommand Tests

        [TestMethod]
        public void CreatePersistCommand_WhenEnforceConcurrencyIsTrue_CreatesPersistCommandWithEnforceConcurrency()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Persist(person, true);

            // Assert
            var command = unitOfWork.Command as PersistCommand;
            Assert.IsNotNull(command);
            Assert.IsTrue(command.EnforceConcurrency);
        }

        private void SetupRepositoryMapIndex(IMock<IRepositoryMapIndex> repositoryMapIndex)
        {
            var map = new MapIndex().GetRepositoryMapIndex("simple").GetMap(typeof (Person));
            repositoryMapIndex.Expect(r => r.GetMap(typeof (Person))).Returns(map);
        }

        [TestMethod]
        public void CreatePersistCommand_WhenEnforceConcurrencyIsFalse_CreatesPersistCommandWithoutEnforceConcurrency()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Persist(person, false);

            // Assert
            var command = unitOfWork.Command as PersistCommand;
            Assert.IsNotNull(command);
            Assert.IsFalse(command.EnforceConcurrency);
        }

        [TestMethod]
        public void CreatePersistCommand_CallsExecuteCommand()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Persist(person, true);

            // Assert
            Assert.AreEqual(1, unitOfWork.ExecuteCount);
        }

        #endregion

        #region CreateDeleteCommand Tests

        [TestMethod]
        public void CreateDeleteCommand_WhenConcurrencyIsTrue_CreatesADeleteCommandWithConcurrency()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Delete(person, false, true);

            // Assert
            var command = unitOfWork.Command as DeleteCommand;
            Assert.IsNotNull(command);
            Assert.IsTrue(command.EnforceConcurrency);
        }

        [TestMethod]
        public void CreateDeleteCommand_WhenConcurrencyIsFalse_CreatesADeleteCommandWithoutConcurrency()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Delete(person, false, false);

            // Assert
            var command = unitOfWork.Command as DeleteCommand;
            Assert.IsNotNull(command);
            Assert.IsFalse(command.EnforceConcurrency);
        }

        [TestMethod]
        public void CreateDeleteCommand_WhenRecursiveIsTrue_CreatesADeleteCommandWithRecursion()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Delete(person, true, false);

            // Assert
            var command = unitOfWork.Command as DeleteCommand;
            Assert.IsNotNull(command);
            Assert.IsTrue(command.Recursive);
        }

        [TestMethod]
        public void CreateDeleteCommand_WhenRecursiveIsFalse_CreatesADeleteCommandWithoutRecursion()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Delete(person, false, false);

            // Assert
            var command = unitOfWork.Command as DeleteCommand;
            Assert.IsNotNull(command);
            Assert.IsFalse(command.Recursive);
        }

        [TestMethod]
        public void CreateDeleteCommand_CallsExecuteCommand()
        {
            // Arrange
            var repositoryMapIndex = new Mock<IRepositoryMapIndex>();
            var dataInterface = new Mock<IDataInterface>();
            var person = new Person();
            SetupRepositoryMapIndex(repositoryMapIndex);

            // Act
            var unitOfWork = new UnitOfWorkHarness(repositoryMapIndex.Object, dataInterface.Object);
            unitOfWork.Delete(person, true, false);

            // Assert
            Assert.AreEqual(1, unitOfWork.ExecuteCount);
        }

        #endregion

        #region Nested Types

        public class UnitOfWorkHarness : UnitOfWorkBase
        {
            public int ExecuteCount { get; set; }
            public ICommand Command { get; set; }

            public UnitOfWorkHarness(IRepositoryMapIndex repositoryMapIndex, IDataInterface dataInterface)
                : base(repositoryMapIndex, dataInterface)
            {
            }

            protected override void ExecuteCommand(ICommand command)
            {
                ExecuteCount++;
                Command = command;
            }
        }

        #endregion
    }
}